﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using LevelEditor.Model.Entity;
using LevelEditor.View.Controls.Adorners;
using System.Collections;
using System.IO;
using LevelEditor.Utils;
using System.ComponentModel;

namespace LevelEditor.View.UserControls
{
    /// <summary>
    /// Logique d'interaction pour ScenePanel.xaml
    /// </summary>
    public partial class ScenePanel : UserControl
    {
        //Events
        public event EventHandler SpriteAdded;
        public event EventHandler SpriteSelected;
        public event EventHandler SpriteDeselected;
        public event EventHandler SpriteDeleted;

        private bool physicEditorMode;
        public bool PhysicEditorMode
        {
            get {
                return physicEditorMode;
            }
            set {
               physicEditorMode = value;
               ShowHidePhysicBodies(physicEditorMode);
            }
        }

        protected void OnSpriteAdded(object sender)
        {
            if (SpriteAdded != null)
                SpriteAdded(sender, new EventArgs());
        }
        protected void OnSpriteSelected(object sender)
        {
            if (SpriteSelected != null)
                SpriteSelected(sender, new EventArgs());
        }
        protected void OnSpriteDeleted(object sender)
        {
            if (SpriteDeleted != null)
                SpriteDeleted(sender, new EventArgs());
        }
        protected void OnSpriteDeselected()
        {
            if (SpriteDeselected != null)
                SpriteDeselected(null, new EventArgs());
        }

        //Members & Properties
        private Scene scene;
        public Scene Scene
        {
            get { return scene; }
            set
            {
                scene = value;
                scene.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(scene_PropertyChanged);

                Size newSize = new Size(scene.Width, scene.Height);

                SceneCanvas.Background = new SolidColorBrush(scene.BackgroundColor);
                SceneCanvas.Width = scene.Width;
                SceneCanvas.Height = scene.Height;
                SceneCanvas.Children.Clear();

                string appPath = FileUtil.AppStartUpPath;

                for (int i = 0; i < scene.Layers.Count; i++)
                {
                    for (int j = 0; j < scene.Layers[i].GameSprites.Count; j++)
                    {
                        GameSprite model = scene.Layers[i].GameSprites[j];
                        MemoryStream ms = new MemoryStream();
                        FileStream stream = new FileStream(appPath + model.ImageSource.OriginalString, FileMode.Open, FileAccess.Read);
                        ms.SetLength(stream.Length);
                        stream.Read(ms.GetBuffer(), 0, (int)stream.Length);
                        ms.Flush();
                        stream.Close();

                        BitmapImage img = new BitmapImage();
                        img.BeginInit();
                        img.StreamSource = ms;
                        img.CacheOption = BitmapCacheOption.OnLoad;
                        img.EndInit();

                        AddNewSprite(model, img);
                    }
                    for (int j = 0; j < scene.Layers[i].PhysicBodies.Count; j++)
                    {
                        PhysicBody body = scene.Layers[i].PhysicBodies[j];
                        AddPhysicBody(body);
                    }
                }

                SceneCanvas.InvalidateMeasure();
                SceneCanvas.InvalidateArrange();
                SceneCanvas.InvalidateVisual();
                SceneCanvas.Measure(newSize);
                SceneCanvas.Arrange(new Rect(newSize));

                scene.ArrangeZOrder();
                ArrangeSpriteZIndex();

                previewImageDirty = true;
                previewImage = PreviewImage;
            }
        }

        public Image SpriteBrush { get; set; }
        public Image SelectedSprite { get; set; } //Maybe select only the first element in the SelectedSprites Array instead
        public List<Image> SelectedSprites { get; set; }

        private GridAdorner gridAdorner;
        private ResizeAdorner resizeAdorner;
        private SelectionAdorner selectionAdorner;

        private Rectangle selectedSpritesBoundingBoxVisualRectangle { get; set; }

        private Rect selectedSpritesBoundingBoxRect;
        private Rect origSelectedSpritesBoundingBoxRect;

        public bool IsGridVisible { get; set; }

        //Methods
        public ScenePanel()
        {
            InitializeComponent();

            this.Loaded += ScenePanel_Loaded;

            this.SceneCanvas.MouseDown += new MouseButtonEventHandler(SceneCanvas_MouseDown);

            //this.MouseMove += new MouseEventHandler(ScenePanel_MouseMove);
            this.MouseDown += new MouseButtonEventHandler(ScenePanel_MouseDown);

            this.WorkAreaScrollViewer.PreviewKeyDown += new KeyEventHandler(WorkAreaScrollViewer_PreviewKeyDown);
            this.WorkAreaScrollViewer.KeyDown += new KeyEventHandler(WorkAreaScrollViewer_KeyDown);

            SelectedSprites = new List<Image>();
        }

        protected void ScenePanel_Loaded(object sender, RoutedEventArgs e)
        {
            gridAdorner = new GridAdorner(SceneCanvas);
            selectionAdorner = new SelectionAdorner(this, SceneCanvas, WorkArea);
            selectionAdorner.SelectionEnded += selectionAdorner_SelectionEnded;

            AdornerLayer myAdornerLayer = AdornerLayer.GetAdornerLayer(this);
            myAdornerLayer.Add(selectionAdorner);

            selectedSpritesBoundingBoxVisualRectangle = new Rectangle();
            selectedSpritesBoundingBoxVisualRectangle.LayoutUpdated += selectedSpritesBoundingBoxVisualRectangle_LayoutUpdated;
        }
        
        protected void selectedSpritesBoundingBoxVisualRectangle_LayoutUpdated(object sender, EventArgs e)
        {
            HandleSelectionRectangleBoundingBoxMove();
        }

        protected void HandleSelectionRectangleBoundingBoxMove()
        {
            if (SceneCanvas.Children.Contains(selectedSpritesBoundingBoxVisualRectangle)) //To replace with a bool...
            {
                ResizeAdorner adorner = (ResizeAdorner)AdornerLayer.GetAdornerLayer(selectedSpritesBoundingBoxVisualRectangle).GetAdorners(selectedSpritesBoundingBoxVisualRectangle)[0];

                if (adorner.IsTransforming)
                {
                    selectedSpritesBoundingBoxRect.X = (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.LeftProperty);
                    selectedSpritesBoundingBoxRect.Y = (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.TopProperty);
                    selectedSpritesBoundingBoxRect.Width = selectedSpritesBoundingBoxVisualRectangle.Width;
                    selectedSpritesBoundingBoxRect.Height = selectedSpritesBoundingBoxVisualRectangle.Height;

                    if (selectedSpritesBoundingBoxRect.Width < 1)
                        selectedSpritesBoundingBoxRect.Width = 1;
                    if (selectedSpritesBoundingBoxRect.Height < 1)
                        selectedSpritesBoundingBoxRect.Height = 1;

                    double translateX = selectedSpritesBoundingBoxRect.X - origSelectedSpritesBoundingBoxRect.X;
                    double translateY = selectedSpritesBoundingBoxRect.Y - origSelectedSpritesBoundingBoxRect.Y;

                    double scaleXRatio = selectedSpritesBoundingBoxRect.Width / origSelectedSpritesBoundingBoxRect.Width;
                    double scaleYRatio = selectedSpritesBoundingBoxRect.Height / origSelectedSpritesBoundingBoxRect.Height;

                    for (int i = 0; i < SelectedSprites.Count; i++)
                    {
                        GameSprite model = SelectedSprites[i].DataContext as GameSprite;

                        if (scaleXRatio != 1 || scaleYRatio != 1)
                        {
                            model.X = (((model.X - selectedSpritesBoundingBoxRect.X) * scaleXRatio)) + selectedSpritesBoundingBoxRect.X;
                            model.Y = (((model.Y - selectedSpritesBoundingBoxRect.Y) * scaleYRatio)) + selectedSpritesBoundingBoxRect.Y;

                            model.Width = model.Width * scaleXRatio;
                            model.Height = model.Height * scaleYRatio;

                            SelectedSprites[i].SetValue(Canvas.WidthProperty, model.Width);
                            SelectedSprites[i].SetValue(Canvas.HeightProperty, model.Height);
                        }
                        SelectedSprites[i].SetValue(Canvas.LeftProperty, model.X + (translateX * scaleXRatio));
                        SelectedSprites[i].SetValue(Canvas.TopProperty, model.Y + (translateY * scaleYRatio));
                    }

                    origSelectedSpritesBoundingBoxRect = selectedSpritesBoundingBoxRect;
                }
            }
        }

        protected void WorkAreaScrollViewer_KeyDown(object sender, KeyEventArgs e)
        {
            ///?????
            if (e.KeyboardDevice.Modifiers == ModifierKeys.Control || e.Key == Key.Delete)
            {
                this.RaiseEvent(e);
                return;
            }
        }

        protected void WorkAreaScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (SelectedSprite != null || SelectedSprites.Count > 0)
            {
                if (e.KeyboardDevice.Modifiers != ModifierKeys.Control && e.Key != Key.Delete)
                {
                    e.Handled = true;
                    HandleKeyDown(null, e);
                }
            }
        }

        public void SelectSprite(Image image)
        {
            if (SelectedSprite != null)
                DeselectSprite();

            AdornerLayer imageAdornerLayer = AdornerLayer.GetAdornerLayer(image);
            resizeAdorner = new ResizeAdorner(image, SceneCanvas, this);
            resizeAdorner.SnapToGrid = IsGridVisible;
            resizeAdorner.GridSquareSize = scene.GridSquareSize;

            imageAdornerLayer.Add(resizeAdorner);

            SelectedSprite = image;
            previewImageDirty = true;
            OnSpriteSelected(image.DataContext);
        }

        protected void ScenePanel_MouseMove(object sender, MouseEventArgs e)
        {
            
        }

        public void SelectSprite(GameSprite model)
        {
            SelectSprite(GetSpriteByModel(model));
        }
        public Image GetSpriteByModel(GameSprite model)
        {
            Image sprite = null;

            for (int i = 0; i < SceneCanvas.Children.Count; i++)
            {
                if (!(SceneCanvas.Children[i] is Image)) //If its the selected
                    continue;

                if ((SceneCanvas.Children[i] as Image).DataContext == model)
                {
                    sprite = SceneCanvas.Children[i] as Image;
                    break;
                }
            }

            return sprite;
        }
        public void DeselectSprite()
        {
            if (SelectedSprites.Count > 0) {

                //Remove all Sprite Resize Adorners
                for(int i = 0; i < SelectedSprites.Count; i++){
                    AdornerLayer spriteAdornerLayer = AdornerLayer.GetAdornerLayer(SelectedSprites[i]);
                    if (spriteAdornerLayer == null)
                        continue;

                    Adorner[] spriteAdorners = spriteAdornerLayer.GetAdorners(SelectedSprites[i]);
                    for (int j = 0; j < spriteAdorners.Length; j++)
                        spriteAdornerLayer.Remove(spriteAdorners[j]);
                }

                SelectedSprites.Clear();

                //Remove the selected Sprites Bounding Box Adorners
                AdornerLayer selectedSpritesAdornerLayer = AdornerLayer.GetAdornerLayer(this.selectedSpritesBoundingBoxVisualRectangle);
                if (selectedSpritesAdornerLayer != null)
                {
                    Adorner[] selectedSpritesAdorners = selectedSpritesAdornerLayer.GetAdorners(this.selectedSpritesBoundingBoxVisualRectangle);

                    for (int i = 0; i < selectedSpritesAdorners.Length; i++)
                        selectedSpritesAdornerLayer.Remove(selectedSpritesAdorners[i]);
                }

                //Remove the Bounding Box
                if (SceneCanvas.Children.Contains(selectedSpritesBoundingBoxVisualRectangle))
                    SceneCanvas.Children.Remove(selectedSpritesBoundingBoxVisualRectangle);

            }

            if (SelectedSprite == null) return;

            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(SelectedSprite);

            if (adornerLayer == null) return;

            Adorner[] adorners = adornerLayer.GetAdorners(SelectedSprite);

            for (int i = 0; i < adorners.Length; i++)
                adornerLayer.Remove(adorners[i]);

            SelectedSprite = null;
            previewImageDirty = true;
            OnSpriteDeselected();

        }
        public void ArrangeSpriteZIndex()
        {
            UIElement[] temp = new UIElement[SceneCanvas.Children.Count];
            SceneCanvas.Children.CopyTo(temp, 0);

            for (int i = scene.Layers.Count - 1; i >= 0; i--)
            {
                for (int j = scene.Layers[i].GameSprites.Count - 1; j >= 0; j--)
                {
                    GameSprite sprite = scene.Layers[i].GameSprites[j];
                    for (int k = 0; k < temp.Length; k++)
                    {
                        if (!(temp[k] is Image))
                            continue;

                        Image image = temp[k] as Image;
                        if (sprite == image.DataContext)
                        {
                            SceneCanvas.Children.Remove(image);
                            SceneCanvas.Children.Add(image);
                            break;
                        }
                    }
                }
            }

            previewImageDirty = true;
        }

        private Rectangle currentPhysicBodyVisual;
        public void DeselectPhysicBody() {

            if (currentPhysicBodyVisual == null)
                return;

            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(currentPhysicBodyVisual);

            if (adornerLayer == null) return;

            Adorner[] adorners = adornerLayer.GetAdorners(currentPhysicBodyVisual);

            if (adorners == null) return;

            for (int i = 0; i < adorners.Length; i++)
                adornerLayer.Remove(adorners[i]);

            currentPhysicBodyVisual = null;
        }


        public void ToggleGrid()
        {
            AdornerLayer myAdornerLayer = AdornerLayer.GetAdornerLayer(SceneCanvas);

            //Hide Grid
            if (IsGridVisible)
            {
                myAdornerLayer.Remove(gridAdorner);
                IsGridVisible = false;
                if (resizeAdorner != null)
                    resizeAdorner.SnapToGrid = false;

                return;
            }

            //Show Grid
            gridAdorner.SquareSize = scene.GridSquareSize;
            if (resizeAdorner != null)
            {
                resizeAdorner.SnapToGrid = true;
                resizeAdorner.GridSquareSize = scene.GridSquareSize;
            }
            myAdornerLayer.Add(gridAdorner);
            IsGridVisible = true;
        }


        private void DeselectSpriteIfClickOutSide(Point mousePos)
        {
            if (SelectedSprite != null)
            {
                Rect spriteRect = new Rect(Canvas.GetLeft(SelectedSprite), Canvas.GetTop(SelectedSprite), SelectedSprite.Width, SelectedSprite.Height);

                if (!spriteRect.Contains(mousePos))
                    DeselectSprite();
            }
        }

        private void PrepareImageBinding(Image image)
        {
            image.Stretch = Stretch.Fill;
            image.MouseDown += new MouseButtonEventHandler(image_MouseDown);

            Binding visibleBinding = new Binding();
            visibleBinding.Source = image.DataContext;
            visibleBinding.Mode = BindingMode.TwoWay;
            visibleBinding.Path = new PropertyPath("IsItemVisible");
            visibleBinding.Converter = new BooleanToVisibilityConverter();

            Binding xBinding = new Binding { Source = image.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("X") };
            Binding yBinding = new Binding { Source = image.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("Y") };
            Binding wBinding = new Binding { Source = image.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("Width") };
            Binding hBinding = new Binding { Source = image.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("Height") };

            BindingOperations.SetBinding(image, Canvas.LeftProperty, xBinding);
            BindingOperations.SetBinding(image, Canvas.TopProperty, yBinding);
            BindingOperations.SetBinding(image, Image.WidthProperty, wBinding);
            BindingOperations.SetBinding(image, Image.HeightProperty, hBinding);
            BindingOperations.SetBinding(image, Image.VisibilityProperty, visibleBinding);
        }
        private void PreparePhysicBodyBinding(Rectangle physicBodyVisual) {
            
            physicBodyVisual.MouseDown += PhysicBodyVisual_MouseDown;

            Binding xBinding = new Binding { Source = physicBodyVisual.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("X") };
            Binding yBinding = new Binding { Source = physicBodyVisual.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("Y") };
            Binding wBinding = new Binding { Source = physicBodyVisual.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("Width") };
            Binding hBinding = new Binding { Source = physicBodyVisual.DataContext, Mode = BindingMode.TwoWay, Path = new PropertyPath("Height") };

            BindingOperations.SetBinding(physicBodyVisual, Canvas.LeftProperty, xBinding);
            BindingOperations.SetBinding(physicBodyVisual, Canvas.TopProperty, yBinding);
            BindingOperations.SetBinding(physicBodyVisual, Image.WidthProperty, wBinding);
            BindingOperations.SetBinding(physicBodyVisual, Image.HeightProperty, hBinding);
        }

        public void RemoveSprite(GameSprite gameSprite)
        {
            Image sprite = null;
            for (int i = 0; i < SceneCanvas.Children.Count; i++)
            {
                if (!(SceneCanvas.Children[i] is Image))
                    continue;

                if ((SceneCanvas.Children[i] as Image).DataContext == gameSprite)
                {
                    sprite = SceneCanvas.Children[i] as Image;
                    break;
                }
            }

            SceneCanvas.Children.Remove(sprite);
            OnSpriteDeleted(gameSprite);
            SelectedSprite = null;
            previewImageDirty = true;
        }

        public void HandleKeyDown(object sender, KeyEventArgs e)
        {
            if (SelectedSprites.Count > 0) //MultipleSprite Selected
            {
                if (Keyboard.IsKeyDown(Key.Up)) selectedSpritesBoundingBoxVisualRectangle.SetValue(Canvas.TopProperty, (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.TopProperty) - 1);
                if (Keyboard.IsKeyDown(Key.Down)) selectedSpritesBoundingBoxVisualRectangle.SetValue(Canvas.TopProperty, (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.TopProperty) + 1);
                if (Keyboard.IsKeyDown(Key.Left)) selectedSpritesBoundingBoxVisualRectangle.SetValue(Canvas.LeftProperty, (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.LeftProperty) - 1);
                if (Keyboard.IsKeyDown(Key.Right)) selectedSpritesBoundingBoxVisualRectangle.SetValue(Canvas.LeftProperty, (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.LeftProperty) + 1);
                
                //Update the orig rectangle
                origSelectedSpritesBoundingBoxRect.X = (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.LeftProperty);
                origSelectedSpritesBoundingBoxRect.Y = (double)selectedSpritesBoundingBoxVisualRectangle.GetValue(Canvas.TopProperty);
                origSelectedSpritesBoundingBoxRect.Width = selectedSpritesBoundingBoxVisualRectangle.Width;
                origSelectedSpritesBoundingBoxRect.Height = selectedSpritesBoundingBoxVisualRectangle.Height;

                for (int i = 0; i < SelectedSprites.Count; i++)
                {
                    GameSprite model = SelectedSprites[i].DataContext as GameSprite;

                    if (Keyboard.IsKeyDown(Key.Up)) SelectedSprites[i].SetValue(Canvas.TopProperty, model.Y - 1);
                    if (Keyboard.IsKeyDown(Key.Down)) SelectedSprites[i].SetValue(Canvas.TopProperty, model.Y + 1);
                    if (Keyboard.IsKeyDown(Key.Left)) SelectedSprites[i].SetValue(Canvas.LeftProperty, model.X - 1);
                    if (Keyboard.IsKeyDown(Key.Right)) SelectedSprites[i].SetValue(Canvas.LeftProperty, model.X + 1);
                }
            }
            else if (SelectedSprite != null)  //Single Selection
            {
                GameSprite model = SelectedSprite.DataContext as GameSprite;

                if (Keyboard.IsKeyDown(Key.Up)) SelectedSprite.SetValue(Canvas.TopProperty, model.Y - 1);
                if (Keyboard.IsKeyDown(Key.Down)) SelectedSprite.SetValue(Canvas.TopProperty, model.Y + 1);
                if (Keyboard.IsKeyDown(Key.Left)) SelectedSprite.SetValue(Canvas.LeftProperty, model.X - 1);
                if (Keyboard.IsKeyDown(Key.Right)) SelectedSprite.SetValue(Canvas.LeftProperty, model.X + 1);
            }
        }

        public void Zoom(double zoomValue)
        {
            double scale = zoomValue / 100;
            if (double.IsNegativeInfinity(scale) || double.IsPositiveInfinity(scale))
                return;

            PresentationSource source = PresentationSource.FromVisual(this);

            double dpiX = 96;
            double dpiY = 96;
            if (source != null)
            {
                dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
                dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
            }

            SceneCanvas.LayoutTransform = new ScaleTransform(scale * (96 / dpiX), scale * (96 / dpiY));
        }

        private bool previewImageDirty;
        private RenderTargetBitmap previewImage;
        public RenderTargetBitmap PreviewImage
        {
            get
            {
                if (previewImageDirty || previewImage == null)
                {
                    double scale = (double)SceneCanvas.LayoutTransform.GetValue(ScaleTransform.ScaleXProperty);
                    double w = scale * SceneCanvas.Width;
                    double h = scale * SceneCanvas.Height;

                    previewImage = new RenderTargetBitmap(Convert.ToInt32(w), Convert.ToInt32(h), 96, 96, PixelFormats.Pbgra32);
                    previewImage.Render(SceneCanvas);
                    previewImage.Freeze();

                    previewImageDirty = false;
                }

                return previewImage;
            }
        }

        private void ShowHidePhysicBodies(bool visible)
        {
            for (int i = 0; i < this.SceneCanvas.Children.Count; i++)
            {
                if (!(this.SceneCanvas.Children[i] is Rectangle))
                    continue;

                Rectangle physicVisualBody = this.SceneCanvas.Children[i] as Rectangle;
                if (!(physicVisualBody.DataContext is PhysicBody))
                    continue;

                physicVisualBody.Visibility = visible ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden;
            }
        }

        // Event Handlers
        private bool SelectionStarted { get; set; }
        protected void ScenePanel_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Point mousePos = e.MouseDevice.GetPosition(SceneCanvas);
            DeselectSpriteIfClickOutSide(mousePos);

            //Start Selection


        }

        //TODO: remove/refactor the sprite creation
        //TODO: move click behaviours to the selection ended
        protected void SceneCanvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.InvalidateVisual();

            Point mousePos = e.MouseDevice.GetPosition(SceneCanvas);
            
            //Cancel the event if clicking on the selectionSpritesBBoxRectangle
            if (SceneCanvas.Children.Contains(selectedSpritesBoundingBoxVisualRectangle) && selectedSpritesBoundingBoxRect.Contains(mousePos))
            {
                //e.Handled = true;
                //selectionAdorner.Disabled = true;
                return;
            }

            //TODO: Dont add sprite if the layer is locked

            if (SelectedSprite != null)
            {
                DeselectSpriteIfClickOutSide(mousePos);
                return;
            }

            if (SpriteBrush == null)
            {
                //FIXME:
                OnSpriteDeselected(); //To Make an explicit call to select Scene...
                return;
            }

            //Only add new sprite if holding Alt Key 
            if (Keyboard.IsKeyDown(Key.LeftAlt))
            {
                //selectionAdorner.Disabled = true;

                GameSprite model = ((GameSprite)SpriteBrush.DataContext).Clone();
                model.IsItemVisible = true;

                Image image = AddNewSprite(model, SpriteBrush.Source);

                model.X = Math.Round(mousePos.X - (image.Width / 2));
                model.Y = Math.Round(mousePos.Y - (image.Height / 2));
                image.SetValue(Canvas.LeftProperty, model.X);
                image.SetValue(Canvas.TopProperty, model.Y);

                previewImageDirty = true;
                OnSpriteAdded(image.DataContext);
            }
        }

        private Image AddNewSprite(GameSprite model, ImageSource source)
        {
            Image image = new Image();
            image.Source = source;
            image.DataContext = model;
            image.Width = ((BitmapSource)source).PixelWidth;
            image.Height = ((BitmapSource)source).PixelHeight;

            if (model.Width == 0 && model.Height == 0)
            {
                model.Width = image.Width;
                model.Height = image.Height;
            }
            else
            {
                image.Width = model.Width;
                image.Height = model.Height;
            }

            image.SetValue(Canvas.LeftProperty, model.X);
            image.SetValue(Canvas.TopProperty, model.Y);

            PrepareImageBinding(image);

            SceneCanvas.Children.Add(image);

            return image;
        }

        private Rectangle AddPhysicBody(PhysicBody body) {

            Rectangle physicBodyVisual = new Rectangle();
            physicBodyVisual.DataContext = body;
            physicBodyVisual.Width = body.Width;
            physicBodyVisual.Height = body.Height;
            physicBodyVisual.Fill = Brushes.Blue;
            physicBodyVisual.Opacity = .5;
            physicBodyVisual.SetValue(Canvas.ZIndexProperty, 100000); //Body Rectangle are always on top that way

            PreparePhysicBodyBinding(physicBodyVisual);

            SceneCanvas.Children.Add(physicBodyVisual);

            return physicBodyVisual;
        }

        protected void image_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //selectionAdorner.Disabled = true;

            GameSprite model = ((Image)sender).DataContext as GameSprite;

            if (model.IsItemLocked || SelectedSprites.Contains(sender))
            {
                return;
            }

            //TODO: Add sprite to selection using SHIFT+Click
            //bool addToSelection = Keyboard.IsKeyDown(Key.LeftCtrl);

            SelectSprite((Image)sender);

            e.Handled = true;
        }

        protected void scene_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "GridSquareSize")
            {
                gridAdorner.SquareSize = scene.GridSquareSize;
                resizeAdorner.GridSquareSize = scene.GridSquareSize;
            }

            if (e.PropertyName == "Width")
                SceneCanvas.SetValue(Canvas.WidthProperty, scene.Width);

            if (e.PropertyName == "Height")
                SceneCanvas.SetValue(Canvas.HeightProperty, scene.Height);

            if (e.PropertyName == "BackgroundColor")
                SceneCanvas.Background = new SolidColorBrush(scene.BackgroundColor);

            previewImageDirty = true;
        }

        Image clipboardSprite = null;
        bool CutRequested = false;

        //As all clicking action trigger this method, ill probably handle everything related to Click, Selection, MouseDown, MouseUp here
        protected void selectionAdorner_SelectionEnded(object sender, EventArgs e)
        {
            Rect selRect = selectionAdorner.SelectionRectangleRelativeToScene;

            //if the selection adorner trigger SelectionEnded even if we are clicking on the selection rectangle
            if (SceneCanvas.Children.Contains(selectedSpritesBoundingBoxVisualRectangle) && origSelectedSpritesBoundingBoxRect.Contains(new Point(selRect.X, selRect.Y)))
                return;

            //Unselect current selected sprite(s)
            DeselectSprite();

            //In Physic Edition Mode, the behaviour is different, we simply create a Rect that will represent the physic body based on the selection rectangle:
            if (PhysicEditorMode) {

                DeselectPhysicBody();

                if (selRect.Width > 0 && selRect.Height > 0)
                    CreatePhysicBody(selRect);

                return;
            }

            //For calculating the bounding box of all selected item in one pass...
            double minX = Double.MaxValue;
            double maxX = Double.MinValue;
            double minY = Double.MaxValue;
            double maxY = Double.MinValue;
            
            //Select all sprite whithing the selection rectangle except those lock or on a locked layer
            for( int i = 0 ; i  < Scene.Layers.Count; i++)
            {
                if( Scene.Layers[i].IsItemLocked || !Scene.Layers[i].IsItemVisible)
                    continue;
                
                for( int j = 0 ; j < Scene.Layers[i].GameSprites.Count; j++)
                {
                    GameSprite gameSprite = Scene.Layers[i].GameSprites[j];
                    Rect gameSpriteRect = new Rect(gameSprite.X, gameSprite.Y, gameSprite.Width, gameSprite.Height);

                    if (selRect.Contains(gameSpriteRect))
                    {
                        Image sprite = GetSpriteByModel(gameSprite);
                        SelectedSprites.Add(sprite);

                        //Bounding box calculation
                        if (gameSpriteRect.X < minX)
                            minX = gameSpriteRect.X;
                        if (gameSpriteRect.Y < minY)
                            minY = gameSpriteRect.Y;
                        if (gameSpriteRect.X + gameSpriteRect.Width > maxX)
                            maxX = gameSpriteRect.X + gameSpriteRect.Width;
                        if (gameSpriteRect.Y + gameSpriteRect.Height > maxY)
                            maxY = gameSpriteRect.Y + gameSpriteRect.Height;
                    }
                }
            }

            if (SelectedSprites.Count == 0)
            {
                return;
            }

            //Highlight && And Create Global Selection Bounding Box for SelectedSprites
            if (!SceneCanvas.Children.Contains(selectedSpritesBoundingBoxVisualRectangle))
                SceneCanvas.Children.Add(selectedSpritesBoundingBoxVisualRectangle);

            selectedSpritesBoundingBoxVisualRectangle.SetValue(Canvas.LeftProperty, minX);
            selectedSpritesBoundingBoxVisualRectangle.SetValue(Canvas.TopProperty, minY);
            selectedSpritesBoundingBoxVisualRectangle.Width = maxX - minX;
            selectedSpritesBoundingBoxVisualRectangle.Height = maxY - minY;

            AdornerLayer myAdornerLayer = AdornerLayer.GetAdornerLayer(selectedSpritesBoundingBoxVisualRectangle);
            resizeAdorner = new ResizeAdorner(selectedSpritesBoundingBoxVisualRectangle, SceneCanvas, this);
            resizeAdorner.SnapToGrid = IsGridVisible;
            resizeAdorner.GridSquareSize = scene.GridSquareSize;
            resizeAdorner.IsHitTestVisible = true;

            myAdornerLayer.Add(resizeAdorner);

            origSelectedSpritesBoundingBoxRect = new Rect(minX, minY, maxX - minX, maxY - minY);

            //Add Selection Adorner For each selected Sprite but DISABLE the functionality
            foreach (Image sprite in SelectedSprites) {
                AdornerLayer spriteAdornerLayer = AdornerLayer.GetAdornerLayer(sprite);
                ResizeAdorner sResizeAdorner = new ResizeAdorner(sprite, SceneCanvas, this);
                sResizeAdorner.SnapToGrid = IsGridVisible;
                sResizeAdorner.GridSquareSize = scene.GridSquareSize;
                sResizeAdorner.DisabledFunctionality = true;

                spriteAdornerLayer.Add(sResizeAdorner);
            }
        }

        private void CreatePhysicBody(Rect selRect)
        {
            PhysicBody body = new PhysicBody();
            body.X = selRect.X;
            body.Y = selRect.Y;
            body.Width = selRect.Width;
            body.Height = selRect.Height;

            Scene.Layers[2].PhysicBodies.Add(body); //FIXME: For now only the playground layer have Physic bodies anyways...

            currentPhysicBodyVisual = AddPhysicBody(body);
        }

        protected void PhysicBodyVisual_MouseDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            DeselectPhysicBody();

            Rectangle physicBodyVisual = sender as Rectangle;

            AdornerLayer bodyRectAdornerLayer = AdornerLayer.GetAdornerLayer(physicBodyVisual);
            resizeAdorner = new ResizeAdorner(physicBodyVisual, SceneCanvas, this);
            resizeAdorner.SnapToGrid = IsGridVisible;
            resizeAdorner.GridSquareSize = scene.GridSquareSize;

            bodyRectAdornerLayer.Add(resizeAdorner);

            currentPhysicBodyVisual = physicBodyVisual;
        }

        //Commands Handlers
        public void CutGameSpriteCmd_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            clipboardSprite = SelectedSprite;
            SceneCanvas.Children.Remove(SelectedSprite); //Or Call RemoveSprite...
            CutRequested = true;

            OnSpriteDeleted(SelectedSprite.DataContext);

            SelectedSprite = null;
        }
        public void CutGameSpriteCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = SelectedSprite != null;
        }

        public void CopyGameSpriteCmd_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            clipboardSprite = SelectedSprite;
        }
        public void CopyGameSpriteCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = SelectedSprite != null;
        }

        public void PasteGameSpriteCmd_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Image image = null;
            if (!CutRequested)
            {
                GameSprite newModel = (clipboardSprite.DataContext as GameSprite).Clone();
                image = new Image();

                image.DataContext = newModel;
                image.Source = clipboardSprite.Source;
                image.Width = newModel.Width;
                image.Height = newModel.Height;

                image.SetValue(Canvas.LeftProperty, newModel.X);
                image.SetValue(Canvas.TopProperty, newModel.Y);

                PrepareImageBinding(image);
            }
            else
            {
                image = clipboardSprite;
                CutRequested = false;
            }

            SceneCanvas.Children.Add(image);
            OnSpriteAdded(image.DataContext);
        }
        public void PasteGameSpriteCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = clipboardSprite != null;
        }

        public void ToggleGridCmd_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            ToggleGrid();
        }
        public void ToggleGridCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        public void DeselectGameSpriteCmd_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DeselectSprite();
            DeselectPhysicBody();
        }
        public void DeselectGameSpriteCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = SelectedSprite != null || currentPhysicBodyVisual != null;
        }

        public void DeleteGameSpriteCmd_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (SelectedSprite != null)
            {
                SceneCanvas.Children.Remove(SelectedSprite); //Or Call RemoveSprite...

                this.scene.RemoveGameSprite(SelectedSprite.DataContext as GameSprite);

                OnSpriteDeleted(SelectedSprite.DataContext);

                SelectedSprite = null;
            }
            if (currentPhysicBodyVisual != null)
            {
                SceneCanvas.Children.Remove(currentPhysicBodyVisual);

                this.scene.Layers[2].PhysicBodies.Remove(currentPhysicBodyVisual.DataContext as PhysicBody); //FIXME: index 2 = Playground layer

                //OnPhysicBodyDeleted(SelectedSprite.DataContext);

                currentPhysicBodyVisual = null;
            }
        }
        public void DeleteGameSpriteCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = SelectedSprite != null || currentPhysicBodyVisual != null;
        }
    
    }
}
